﻿/*
 * Copyright (C) 2009 Gentea Alexandru <in.10se@yahoo.com>
 * 
 *
 * This file is part of WordNet::SQLConverter
 * See http://wnsqlconverter.codeplex.com/ for details.
 *
 * WordNet::SQLConverter is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * WordNet::SQLConverter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
using System;
using System.ComponentModel;
using System.Configuration;
using System.Windows.Forms;
using GDALib;
using NLog;

namespace WordNet.SQLConverter
{
    public partial class Main : Form
    {
        DatabaseBuilder dbBuilder;
        SQLConverter wordNetConverter;
        DatabaseAccessor dbAccessor;
        String scriptsPath;
        int maxRetries, timeout;
        Logger logger;
        bool userCancelation;
        
        public Main()
        {
            InitializeComponent();
            //set available db types
            sqlTypeCB.Items.Add(DbType.Oracle);
            sqlTypeCB.Items.Add(DbType.Mysql);
            sqlTypeCB.Items.Add(DbType.Postgresql);
            sqlTypeCB.Items.Add(DbType.SQLite);
            sqlTypeCB.Items.Add(DbType.SqlServer);
            sqlTypeCB.SelectedIndex = 4;

            //set up background worker
            backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
            backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
            backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
            backgroundWorker.WorkerSupportsCancellation = true;
            backgroundWorker.WorkerReportsProgress = true;

            logger = LogManager.GetCurrentClassLogger();
            folderBrowserDialog.ShowNewFolderButton = false;
            ReadSettings();
        }

        private void convertBtn_Click(object sender, EventArgs e)
        {
            //sql engine
            if (serverTB.Text.Length == 0)
            {
                MessageBox.Show("Please specify the SQL server name.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            if (intSecCB.Checked == false && userTB.Text.Length == 0)
            {
                MessageBox.Show("Please specify the username to be used.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            if (intSecCB.Checked == false && passTB.Text.Length == 0)
            {
                MessageBox.Show("Please specify the password for the given username.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            //input data
            if (wordNetPathTB.Text.Length == 0)
            {
                MessageBox.Show("Please specify the path to the WordNet directory.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            //output data
            if (dbNameTB.Text.Length == 0)
            {
                MessageBox.Show("Please specify the output database name.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            if (dbPathTB.Text.Length == 0)
            {
                MessageBox.Show("Please specify a writable path for the database files.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            String connectionString = String.Format("Data Source={0}; Initial Catalog=;Integrated Security={1};" + 
                                        "User ID={2};Password={3};Min Pool Size=5;",
                                        serverTB.Text, intSecCB.Checked, userTB.Text, passTB.Text);

            statusLabel.Text += "Connecting to database..." + Environment.NewLine;
            dbAccessor = new DatabaseAccessor(connectionString, new LogExceptionFunction(logger.Error), timeout);
            
            if (dbAccessor.TestConnection())
            {
                SwitchControlsStates();
                object args = new object[] { dbNameTB.Text, dbPathTB.Text, sqlTypeCB.SelectedItem,
                    System.IO.Path.Combine(wordNetPathTB.Text, "dict"), verbNetPathTB.Text };
                userCancelation = false;
                backgroundWorker.RunWorkerAsync(args);
            }
            else
            {
                MessageBox.Show("Failed to establish a connection to the SQL server. Check the log for details.",
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker workerThread = sender as BackgroundWorker;
            dbBuilder = new DatabaseBuilder(dbAccessor, scriptsPath);
            object[] args = (object[])e.Argument;

            if (workerThread.CancellationPending)
            {
                e.Cancel = true;
                return;
            }

            if (dbBuilder.BuildDatabase((String)args[0], (String)args[1], (DbType)args[2], workerThread, e))
            {
                wordNetConverter = new SQLConverter(dbAccessor, (String)args[3], (String)args[4], workerThread, e);
                e.Result = wordNetConverter.ConvertToSQL(dbNameTB.Text);
            }
            else
                e.Result = false;
        }

        void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                progressBar.Value = 0;
                if(userCancelation)
                    statusLabel.Text = "Conversion canceled by user.";
                else
                    statusLabel.Text = "Conversion canceled due to unrecoverable error(s). Check the log for details.";
            }
            else if ((bool)e.Result)
            {
                statusLabel.Text = "Conversion completed.";
                MessageBox.Show("WordNet to SQL conversion finished.", "Conversion finished",
                    MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                progressBar.Value = 100;
            }
            else
            {
                progressBar.Value = 0;
                MessageBox.Show("Something went wrong during the SQL conversion process. Check the log for details.", "Conversion failed",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                statusLabel.Text = "Conversion failed.";
            }

            SwitchControlsStates();
        }

        void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage;
            statusLabel.Text = (String)e.UserState;
        }

        private void wordNetBtn_Click(object sender, EventArgs e)
        {
            folderBrowserDialog.ShowDialog(); 
            wordNetPathTB.Text = folderBrowserDialog.SelectedPath;
        }

        private void verbNetBtn_Click(object sender, EventArgs e)
        {
            folderBrowserDialog.ShowDialog();
            verbNetPathTB.Text = folderBrowserDialog.SelectedPath;
        }

        private void dbPathBtn_Click(object sender, EventArgs e)
        {
            folderBrowserDialog.ShowDialog();
            dbPathTB.Text = folderBrowserDialog.SelectedPath;
        }

        private void stopBtn_Click(object sender, EventArgs e)
        {
            if (dbAccessor != null)
                dbAccessor.EndBulkExec(true);

            userCancelation = true;
            backgroundWorker.CancelAsync();
        }

        private void SwitchControlsStates()
        {
            bool b = !serverTB.Enabled;

            //switch the state of fields/buttons
            serverTB.Enabled = userTB.Enabled = passTB.Enabled = dbPathBtn.Enabled = b;
            dbPathTB.Enabled = dbNameTB.Enabled = wordNetBtn.Enabled = wordNetPathTB.Enabled = b;
            convertBtn.Enabled = intSecCB.Enabled = exitBtn.Enabled = b;
            
            cancelBtn.Enabled = !b;
        }

        private void ReadSettings()
        {
            String value;

            value = ConfigurationManager.AppSettings["DBAccessorMaxRetries"];
            if (value == null || !int.TryParse(value, out maxRetries))
            {
                maxRetries = 3;
                logger.Error("Failed to get value for DBAccessorMaxRetries. Assuming default value of 3.");
                MessageBox.Show("Failed to get value for DBAccessorMaxRetries. Assuming default value of 3.", "Error",
                     MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            
            value = ConfigurationManager.AppSettings["DBAccessorTimeout"];
            if (value == null || !int.TryParse(value, out timeout))
            {
                timeout = 100;
                logger.Error("Failed to get value for DBAccessorTimeout. Assuming default value of 100.");
                MessageBox.Show("Failed to get value for DBAccessorTimeout. Assuming default value of 100.", "Error",
                     MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            value = ConfigurationManager.AppSettings["ScriptsPath"];
            if (value == null)
            {
                scriptsPath = "../../Scripts";
                logger.Error("Failed to get value for ScriptsPath. Assuming default value of ../../Scripts.");
                MessageBox.Show("Failed to get value for ScriptsPath. Assuming default value of ../../Scripts.", "Error",
                     MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
                scriptsPath = value;
        }

        private void intSecCB_CheckedChanged(object sender, EventArgs e)
        {
            userTB.Enabled = passTB.Enabled = !passTB.Enabled;
        }

        private void exitBtn_Click(object sender, EventArgs e)
        {
            this.Close();
            Application.Exit();
        }
    }
}
